#!/usr/bin/env python3
"""
OAuth2认证流程的实现
支持多种OAuth2提供商（Google, GitHub, Facebook等）
"""

import json
import secrets
from typing import Dict, List, Optional, Any, Tuple
from urllib.parse import urlencode, parse_qs
import requests
from common.api_client import APIClient
from common.logger import get_logger
from common.utils import generate_random_string


class OAuth2Provider:
    """OAuth2提供商配置"""
    
    def __init__(self, name: str, client_id: str, client_secret: str, 
                 authorize_url: str, token_url: str, userinfo_url: str,
                 scope: str = "openid profile email"):
        self.name = name
        self.client_id = client_id
        self.client_secret = client_secret
        self.authorize_url = authorize_url
        self.token_url = token_url
        self.userinfo_url = userinfo_url
        self.scope = scope


class OAuth2Flow:
    """OAuth2认证流程管理器"""
    
    def __init__(self, base_url: str, api_client: Optional[APIClient] = None):
        """
        初始化OAuth2认证流程
        
        Args:
            base_url: 应用基础URL
            api_client: API客户端实例
        """
        self.base_url = base_url.rstrip('/')
        self.api_client = api_client or APIClient(base_url=self.base_url)
        self.logger = get_logger(__name__)
        
        # OAuth2提供商配置
        self.providers: Dict[str, OAuth2Provider] = {}
        
        # 状态存储（生产环境中应使用Redis等持久化存储）
        self.state_store: Dict[str, Dict] = {}
        
        # 回调端点
        self.callback_endpoint = '/api/auth/oauth/callback'
    
    def register_provider(self, provider: OAuth2Provider):
        """
        注册OAuth2提供商
        
        Args:
            provider: OAuth2提供商配置
        """
        self.providers[provider.name.lower()] = provider
        self.logger.info(f"已注册OAuth2提供商: {provider.name}")
    
    def get_authorization_url(self, provider_name: str, redirect_uri: str, 
                            state: Optional[str] = None, **kwargs) -> str:
        """
        获取授权URL
        
        Args:
            provider_name: 提供商名称
            redirect_uri: 回调URI
            state: 状态参数
            **kwargs: 其他参数
        
        Returns:
            授权URL
        """
        provider = self.providers.get(provider_name.lower())
        if not provider:
            raise ValueError(f"未找到提供商: {provider_name}")
        
        # 生成状态参数
        if not state:
            state = generate_random_string(32)
        
        # 存储状态信息
        self.state_store[state] = {
            'provider': provider_name,
            'redirect_uri': redirect_uri,
            'timestamp': self._get_timestamp(),
            **kwargs
        }
        
        # 构建授权URL
        params = {
            'client_id': provider.client_id,
            'redirect_uri': redirect_uri,
            'response_type': 'code',
            'scope': provider.scope,
            'state': state
        }
        
        # 添加额外参数
        params.update(kwargs)
        
        auth_url = f"{provider.authorize_url}?{urlencode(params)}"
        self.logger.info(f"生成授权URL: {provider_name}")
        
        return auth_url
    
    def handle_callback(self, code: str, state: str, error: Optional[str] = None) -> Dict[str, Any]:
        """
        处理OAuth2回调
        
        Args:
            code: 授权码
            state: 状态参数
            error: 错误信息
        
        Returns:
            认证结果
        """
        self.logger.info(f"处理OAuth2回调 - 状态: {state}")
        
        # 检查错误
        if error:
            return {
                'success': False,
                'message': f"OAuth2认证失败: {error}",
                'error': error
            }
        
        # 验证状态参数
        state_info = self.state_store.get(state)
        if not state_info:
            return {
                'success': False,
                'message': "无效的状态参数",
                'error': 'invalid_state'
            }
        
        # 清理状态存储
        del self.state_store[state]
        
        provider_name = state_info['provider']
        provider = self.providers.get(provider_name.lower())
        
        if not provider:
            return {
                'success': False,
                'message': f"未知的OAuth2提供商: {provider_name}",
                'error': 'unknown_provider'
            }
        
        try:
            # 交换访问令牌
            token_data = self._exchange_code_for_token(code, provider, state_info['redirect_uri'])
            
            if not token_data.get('access_token'):
                return {
                    'success': False,
                    'message': "获取访问令牌失败",
                    'error': 'token_exchange_failed'
                }
            
            # 获取用户信息
            user_info = self._get_user_info(token_data['access_token'], provider)
            
            # 处理用户信息
            result = self._process_user_info(user_info, provider_name, token_data)
            
            self.logger.info(f"OAuth2认证成功: {provider_name} - {user_info.get('email', 'N/A')}")
            
            return result
            
        except Exception as e:
            self.logger.error(f"OAuth2认证异常: {str(e)}")
            return {
                'success': False,
                'message': f"OAuth2认证失败: {str(e)}",
                'error': str(e)
            }
    
    def _exchange_code_for_token(self, code: str, provider: OAuth2Provider, redirect_uri: str) -> Dict[str, Any]:
        """
        交换访问令牌
        
        Args:
            code: 授权码
            provider: OAuth2提供商
            redirect_uri: 回调URI
        
        Returns:
            令牌数据
        """
        data = {
            'client_id': provider.client_id,
            'client_secret': provider.client_secret,
            'code': code,
            'grant_type': 'authorization_code',
            'redirect_uri': redirect_uri
        }
        
        response = requests.post(
            provider.token_url,
            data=data,
            headers={'Accept': 'application/json'}
        )
        
        if response.status_code != 200:
            raise Exception(f"令牌交换失败: {response.status_code} - {response.text}")
        
        return response.json()
    
    def _get_user_info(self, access_token: str, provider: OAuth2Provider) -> Dict[str, Any]:
        """
        获取用户信息
        
        Args:
            access_token: 访问令牌
            provider: OAuth2提供商
        
        Returns:
            用户信息
        """
        headers = {
            'Authorization': f'Bearer {access_token}',
            'Accept': 'application/json'
        }
        
        response = requests.get(provider.userinfo_url, headers=headers)
        
        if response.status_code != 200:
            raise Exception(f"获取用户信息失败: {response.status_code} - {response.text}")
        
        return response.json()
    
    def _process_user_info(self, user_info: Dict[str, Any], provider_name: str, 
                          token_data: Dict[str, Any]) -> Dict[str, Any]:
        """
        处理用户信息
        
        Args:
            user_info: 用户信息
            provider_name: 提供商名称
            token_data: 令牌数据
        
        Returns:
            处理结果
        """
        # 标准化用户信息
        standardized_info = self._standardize_user_info(user_info, provider_name)
        
        # 调用后端API处理OAuth2登录/注册
        oauth_data = {
            'provider': provider_name,
            'user_info': standardized_info,
            'token_data': {
                'access_token': token_data.get('access_token'),
                'refresh_token': token_data.get('refresh_token'),
                'expires_in': token_data.get('expires_in'),
                'token_type': token_data.get('token_type')
            }
        }
        
        # 调用认证API
        response = self.api_client.post('/api/auth/oauth/login', data=oauth_data)
        
        return response
    
    def _standardize_user_info(self, user_info: Dict[str, Any], provider_name: str) -> Dict[str, Any]:
        """
        标准化用户信息
        
        Args:
            user_info: 原始用户信息
            provider_name: 提供商名称
        
        Returns:
            标准化用户信息
        """
        standardized = {}
        
        # 根据提供商映射字段
        if provider_name.lower() == 'google':
            standardized.update({
                'id': user_info.get('sub'),
                'email': user_info.get('email'),
                'name': user_info.get('name'),
                'given_name': user_info.get('given_name'),
                'family_name': user_info.get('family_name'),
                'picture': user_info.get('picture'),
                'locale': user_info.get('locale'),
                'email_verified': user_info.get('email_verified', False)
            })
        elif provider_name.lower() == 'github':
            standardized.update({
                'id': user_info.get('id'),
                'email': user_info.get('email'),
                'name': user_info.get('name'),
                'login': user_info.get('login'),
                'avatar_url': user_info.get('avatar_url'),
                'bio': user_info.get('bio'),
                'location': user_info.get('location')
            })
        elif provider_name.lower() == 'facebook':
            standardized.update({
                'id': user_info.get('id'),
                'email': user_info.get('email'),
                'name': user_info.get('name'),
                'first_name': user_info.get('first_name'),
                'last_name': user_info.get('last_name'),
                'picture': user_info.get('picture', {}).get('data', {}).get('url')
            })
        else:
            # 通用映射
            standardized.update(user_info)
        
        # 确保必要字段
        if not standardized.get('email') and user_info.get('email'):
            standardized['email'] = user_info['email']
        
        if not standardized.get('name') and user_info.get('name'):
            standardized['name'] = user_info['name']
        
        return standardized
    
    def refresh_token(self, provider_name: str, refresh_token: str) -> Dict[str, Any]:
        """
        刷新访问令牌
        
        Args:
            provider_name: 提供商名称
            refresh_token: 刷新令牌
        
        Returns:
            新的令牌数据
        """
        provider = self.providers.get(provider_name.lower())
        if not provider:
            return {
                'success': False,
                'message': f"未知的OAuth2提供商: {provider_name}"
            }
        
        try:
            data = {
                'client_id': provider.client_id,
                'client_secret': provider.client_secret,
                'refresh_token': refresh_token,
                'grant_type': 'refresh_token'
            }
            
            response = requests.post(provider.token_url, data=data)
            
            if response.status_code != 200:
                return {
                    'success': False,
                    'message': f"令牌刷新失败: {response.status_code}"
                }
            
            token_data = response.json()
            
            return {
                'success': True,
                'data': token_data
            }
            
        except Exception as e:
            self.logger.error(f"刷新令牌异常: {str(e)}")
            return {
                'success': False,
                'message': f"刷新令牌失败: {str(e)}"
            }
    
    def revoke_token(self, provider_name: str, token: str, token_type_hint: str = 'access_token') -> Dict[str, Any]:
        """
        撤销令牌
        
        Args:
            provider_name: 提供商名称
            token: 要撤销的令牌
            token_type_hint: 令牌类型提示
        
        Returns:
            撤销结果
        """
        # 注意：不是所有提供商都支持令牌撤销
        self.logger.info(f"撤销令牌: {provider_name}")
        
        return {
            'success': True,
            'message': '令牌撤销功能需要提供商支持'
        }
    
    def get_supported_providers(self) -> List[str]:
        """
        获取支持的OAuth2提供商列表
        
        Returns:
            提供商名称列表
        """
        return list(self.providers.keys())
    
    def _get_timestamp(self) -> int:
        """获取当前时间戳"""
        import time
        return int(time.time())


# 预配置的OAuth2提供商
def create_google_provider(client_id: str, client_secret: str) -> OAuth2Provider:
    """创建Google OAuth2提供商"""
    return OAuth2Provider(
        name='google',
        client_id=client_id,
        client_secret=client_secret,
        authorize_url='https://accounts.google.com/o/oauth2/auth',
        token_url='https://oauth2.googleapis.com/token',
        userinfo_url='https://www.googleapis.com/oauth2/v3/userinfo',
        scope='openid email profile'
    )


def create_github_provider(client_id: str, client_secret: str) -> OAuth2Provider:
    """创建GitHub OAuth2提供商"""
    return OAuth2Provider(
        name='github',
        client_id=client_id,
        client_secret=client_secret,
        authorize_url='https://github.com/login/oauth/authorize',
        token_url='https://github.com/login/oauth/access_token',
        userinfo_url='https://api.github.com/user',
        scope='user:email'
    )


def create_facebook_provider(client_id: str, client_secret: str) -> OAuth2Provider:
    """创建Facebook OAuth2提供商"""
    return OAuth2Provider(
        name='facebook',
        client_id=client_id,
        client_secret=client_secret,
        authorize_url='https://www.facebook.com/v12.0/dialog/oauth',
        token_url='https://graph.facebook.com/v12.0/oauth/access_token',
        userinfo_url='https://graph.facebook.com/me?fields=id,name,email',
        scope='email public_profile'
    )


# 使用示例
if __name__ == "__main__":
    # 创建OAuth2管理器
    oauth2 = OAuth2Flow("http://localhost:8000")
    
    # 注册提供商（需要配置实际的client_id和client_secret）
    # google_provider = create_google_provider("your_google_client_id", "your_google_client_secret")
    # oauth2.register_provider(google_provider)
    
    # 测试获取授权URL
    # auth_url = oauth2.get_authorization_url(
    #     provider_name='google',
    #     redirect_uri='http://localhost:8000/api/auth/oauth/callback'
    # )
    # print("授权URL:", auth_url)
    
    print("OAuth2认证流程管理器已初始化")